﻿#include "gobang_ui.h"
#include "gobang.h"
#include <stdlib.h>
#include <string.h>
#include "assets.h"
#ifdef WIN32
#include <windows.h>
#define PlayAudio(name) PlaySound(TEXT(name), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC)
#else
#define PlayAudio(name) 
#endif

struct gobang_ui{
    int state;
    gobang_state* gobang;
    canvas_t canvas;
    //窗口大小
    int width;
    int height;
    //棋盘位置
    int board_x; 
    int board_y;
    int board_width;
    //棋子位置
    int piece_first_x;
    int piece_first_y;
    int piece_distance;
    int piece_radius;

    //正在下子
    int putting_piece_x; //棋子坐标
    int putting_piece_y;     
    bool putting;
    bool putting_white;

    //正在移动棋子
    bool moving; 
    bool moving_white;
    int moving_x; //窗口坐标
    int moving_y;
    int moving_piece_x; //棋子坐标
    int moving_piece_y;   

    //正在关闭
    bool closing;
    int closing_start_x;
    int closing_start_y;
    int closing_end_x;
    int closing_end_y;    
};

static canvas_image_t piece_black_;
static canvas_image_t piece_white_;

 

//屏幕坐标转棋盘坐标
static void coord_win_to_board(gobang_ui* u, int wx, int wy, int* obx, int* oby){
    int disx = wx - u->piece_first_x;
    int disy = wy - u->piece_first_y;
    int bx = disx / u->piece_distance;
    int by = disy / u->piece_distance;
    if((disx % u->piece_distance) >(u->piece_distance / 2))bx ++;
    if((disy % u->piece_distance) >(u->piece_distance / 2))by ++;
    *obx = bx;
    *oby = by; 
}



void gobang_ui_resize(gobang_ui* u, int w, int h){
    int center_x, center_y;
    if(w > h){
        u->board_width = h;
        u->board_x = (w - h) / 2;
        u->board_y = 0;
    }else{
        u->board_width = w;
        u->board_x = 0;
        u->board_x = (h - w) / 2;
    }
    center_x = w / 2;
    center_y = h / 2;
    u->piece_distance = u->board_width / 20;
    u->piece_first_x = center_x - u->piece_distance * 9;
    u->piece_first_y = center_y - u->piece_distance * 9;
    u->piece_radius = (u->piece_distance / 2) - 1; 
    u->width = w;
    u->height = h;       
}


gobang_ui* gobang_ui_init(int w, int h){
    gobang_ui* u = malloc(sizeof(gobang_ui));
    if(!u)return u;    
    memset(u,0,sizeof(gobang_ui));
    gobang_ui_resize(u,w,h);
    u->gobang = gobang_init();
    if(!u->gobang)return NULL; 
    u->state = GOBANG_UI_NEED_UPDATE;
    return u;
}

bool gobang_ui_mouse_down(gobang_ui* u, int x, int y){
    int bx=0,by=0,ret,last;    
    last = gobang_get_state(u->gobang);
    coord_win_to_board(u, x, y, &bx, &by);    
    ret = gobang_put_piece(u->gobang, bx, by);
    if(ret < 0){
        if(ret == GOBANG_ERROR_ALREADY_PIECE){
            u->moving = true;
            u->moving_piece_x = bx;
            u->moving_piece_y = by;
            u->moving_x = x;
            u->moving_y = y;
            u->moving_white = gobang_has_piece(u->gobang, bx, by) == GOBANG_STATE_WHITE_STEP;
            u->state |= GOBANG_UI_NEED_UPDATE;
            return true;
        }else if(ret == GOBANG_ERROR_OUT_BOARD){
            u->closing = true;
            u->closing_start_x = x;
            u->closing_start_y = y;
            u->closing_end_x = x;
            u->closing_end_y = y;            
            u->state |= GOBANG_UI_NEED_UPDATE;
            return true;
        }
        return false;
    }
    if(!u->putting){
        u->putting = true;
        u->putting_piece_x = bx;
        u->putting_piece_y = by;
        u->putting_white = (last == GOBANG_STATE_WHITE_STEP);   
    }
    PlayAudio("CHESS");
    u->state |= GOBANG_UI_NEED_UPDATE;
    return true;
}

static bool check_need_close(gobang_ui* u){
    int dis;
    if(!u->closing)return false;    
    if(u->width > u->height){
        dis = u->closing_end_x - u->closing_start_x;              
    }else{
        dis = u->closing_end_y - u->closing_start_y;        
    }
    if(dis < 0)dis = 0 - dis;  
    if(dis > (u->board_width / 2))return true;
    return false;
}

bool gobang_ui_mouse_up(gobang_ui* u, int x, int y){
    int bx=0,by=0,ret,lastx,lasty;
    if(check_need_close(u)){
        u->state = GOBANG_UI_NEED_QUIT;
        return true;
    }
    if(u->closing){
        u->state = GOBANG_UI_NEED_UPDATE;
        u->closing = false;
        return true;
    }    
    if(!u->moving)return false;
    u->moving = false;
    coord_win_to_board(u,x,y,&bx,&by);
    ret = gobang_get_last_piece(u->gobang, &lastx, &lasty);
    if(ret >= 0 && u->moving_piece_x == lastx && u->moving_piece_y == lasty){
        //移动最后一个棋子 = 悔棋
        ret = gobang_has_piece(u->gobang, bx, by);
        if(ret == 0){ //移到空的位置
            if(0 == gobang_undo(u->gobang)){
                gobang_put_piece(u->gobang, bx, by);                
            }
        }else if(ret == GOBANG_ERROR_OUT_BOARD){  //移到棋盘外
            gobang_undo(u->gobang);
        }else{
            return false;
        }
    }else{
        //不是移动最后一个棋子 
        ret = gobang_has_piece(u->gobang, bx, by);
        if(ret == GOBANG_ERROR_OUT_BOARD){  //移到棋盘外
            gobang_reset(u->gobang);
            u->putting = false;            
        }else{
            return false;
        }
    } 
    u->state |= GOBANG_UI_NEED_UPDATE;
    return true;  
}

bool gobang_ui_mouse_move(gobang_ui* u, int x, int y){
    if(u->moving){
        u->moving_x = x;
        u->moving_y = y;
        u->state |= GOBANG_UI_NEED_UPDATE;
        return true;
    }
    if(u->closing){
        u->closing_end_x = x;
        u->closing_end_y = y;
        u->state |= GOBANG_UI_NEED_UPDATE;
        return true;
    }
    return false;
}


static void render_one_piece(gobang_ui* u, int bx, int by, bool white){
    int x,y;
    canvas_t c = u->canvas;
    x = u->piece_first_x + bx * u->piece_distance;
    y = u->piece_first_y + by * u->piece_distance;
    if(u->moving && u->moving_piece_x == bx && u->moving_piece_y)return;   
    canvas_draw_image(c, white?piece_white_:piece_black_, x - u->piece_radius, y - u->piece_radius, u->piece_radius*2, u->piece_radius*2);
}

static void render_board(gobang_ui* u){
    int i;
    canvas_t c = u->canvas;
    canvas_set_fill_color(c, canvas_color_rgb(235,190,130));
    canvas_fill_rect(c, u->board_x, u->board_y, u->board_width, u->board_width);
    canvas_set_fill_color(c, canvas_color_rgb(0,0,0));
    for (i = 0; i < 19; i++){
        canvas_fill_rect(c, u->piece_first_x + (i * u->piece_distance) - 1, u->piece_first_y, 2, 18*u->piece_distance);
    }
    for (i = 0; i < 19; i++){
        canvas_fill_rect(c, u->piece_first_x, u->piece_first_y + (i * u->piece_distance) - 1, 18*u->piece_distance, 2);
    }
}

static void load_image(canvas_t c){    
    piece_black_ = canvas_create_image(c, _binary_assets_piece_black_bgra_start, 36, 36);
    piece_white_ = canvas_create_image(c, _binary_assets_piece_white_bgra_start, 36, 36);
}

static void render_closing(gobang_ui* u, canvas_t c){
    int dis;
    if(u->width > u->height){
        dis = u->closing_end_x - u->closing_start_x; 
        if(dis < 0)dis = 0 - dis;
        canvas_set_fill_color(c, canvas_color_rgb(0,0,0));
        canvas_fill_rect(c, u->board_x, u->board_y, dis, u->board_width);
        canvas_fill_rect(c, u->board_x + u->board_width - dis, u->board_y, dis, u->board_width);
    }else{
        dis = u->closing_end_y - u->closing_start_y; 
        if(dis < 0)dis = 0 - dis;
        canvas_set_fill_color(c, canvas_color_rgb(0,0,0));
        canvas_fill_rect(c, u->board_x, u->board_y, u->board_width, dis);
        canvas_fill_rect(c, u->board_x, u->board_y + u->board_width - dis, u->board_width, dis);
    }
}

void gobang_ui_update(gobang_ui* u, canvas_t c){    
    u->canvas = c;    
    u->state &= ~GOBANG_UI_NEED_UPDATE;
    if(!piece_white_){
        load_image(c);
    }
    if(u->putting){
        render_one_piece(u, u->putting_piece_x, u->putting_piece_y, u->putting_white);
        u->putting = false;
        return;
    }

    canvas_clear(c, canvas_color_rgb(0,0,0));
    render_board(u);
    gobang_enum_pieces(u->gobang, (gobang_enum_pieces_callback)render_one_piece, u);
    if(u->moving){
        canvas_draw_image(c, u->moving_white?piece_white_:piece_black_, u->moving_x - u->piece_radius, u->moving_y - u->piece_radius, u->piece_radius*2, u->piece_radius*2);            
    }
    if(u->closing){
        render_closing(u, c);
    }
}

int gobang_ui_state(gobang_ui* u){
    return u->state;
}

